# 
#  PySL:  Use S-Lang from Python (and vice versa)
#  Copyright (C) 2005,2006 Christopher J. Stawarz <chris@pseudogreen.org>
# 
#  This program is free software; you can redistribute it and/or modify
#  it under the terms of the GNU General Public License as published by
#  the Free Software Foundation; either version 2 of the License, or
#  (at your option) any later version.
# 
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
# 
#  You should have received a copy of the GNU General Public License
#  along with this program; if not, write to the Free Software
#  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
# 


"""
Use S-Lang from Python (and vice versa)
"""


import copy_reg
from pickle import PicklingError, UnpicklingError
import _pysl
from _pysl import *
from _pysl import __version__


__all__ = tuple([name for name in dir(_pysl) if not name.startswith('_')] +
                ['SLangMarshaller', 'sl'])


def construct_slangobject(refname):
   obj = sl.__get_reference(refname)
   if obj is None:
      raise UnpicklingError("cannot find S-Lang object '%s'" % refname)
   return obj


def reduce_slangobject(obj):
   if not sl.__is_same(sl.typeof(obj), sl.Ref_Type):
      raise PicklingError('S-Lang object is not a reference and cannot ' +
                          'be pickled')
   refname = repr(obj)[1:]
   if 0 == is_defined(refname):
      raise PicklingError('only global S-Lang references can be pickled')
   return (construct_slangobject, (refname,))


copy_reg.constructor(construct_slangobject)
copy_reg.pickle(SLangObject, reduce_slangobject)


class SLangMarshaller(object):
   """
   Module-like interface to S-Lang functions and variables

   The SLangMarshaller class provides a convenient, module-like
   interface to S-Lang functions and variables.  By default, a
   SLangMarshaller will search for names in S-Lang's global namespace.
   However, if the constructor is passed a namespace name, then only
   that namespace will be searched.  The object pysl.sl is a
   predefined SLangMarshaller for the S-Lang global namespace.

   Existing S-Lang functions and variables are accessed as attributes
   of the SLangMarshaller object.  To set the value of a S-Lang
   variable, assign to the appropriate attribute.  If the S-Lang
   variable does not exist, it will be declared automatically.

   In addition, functions and variables in S-Lang namespaces can be
   accessed via the same syntax used for components of Python
   packages, providing a very natural-looking namespace hierarchy.
   """

   def __init__(self, ns_name=None):
      self.__dict__['_ns_name'] = ns_name

   def __setattr__(self, name, value):
      if name in self.__dict__:
         object.__setattr__(self, name, value)
         return

      isdef = is_defined(name, self._ns_name)

      if isdef > 0:
         raise SLangError("'%s' is the name of a S-Lang function" % name)

      if isdef == 0:
         load_string("variable %s;" % name, self._ns_name)
         
      push(value)
      load_string("%s = ();" % name, self._ns_name)

   def __getattr__(self, name):
      isdef = is_defined(name, self._ns_name)

      if isdef == 0:
         if name not in sl._get_namespaces():
            raise AttributeError("'%s' object has no attribute '%s'" %
                                 (type(self).__name__, name))
         ns = SLangMarshaller(name)
         self.__dict__[name] = ns
         return ns

      if isdef > 0:
         name = '&' + name

      load_string(name, self._ns_name)
      return pop()


# Create the default SLangMarshaller and add the S-Lang import() function to
# it with the name "slimport"
sl = SLangMarshaller()
sl.__dict__['slimport'] = getattr(sl, 'import')


#
# Map the standard S-Lang errors to Python exceptions
#

for slerr, pyerr in \
    ((sl.AnyError, Exception),
     (sl.OSError, OSError),
     (sl.MallocError, MemoryError),
     (sl.ImportError, ImportError),
     (sl.ParseError, SyntaxError),
     (sl.SyntaxError, SyntaxError),
     (sl.DuplicateDefinitionError, SyntaxError),
     (sl.UndefinedNameError, NameError),
     (sl.RunTimeError, RuntimeError),
     (sl.InvalidParmError, ValueError),
     (sl.TypeMismatchError, TypeError),
     (sl.UserBreakError, KeyboardInterrupt),
     (sl.StackError, RuntimeError),
     (sl.StackOverflowError, RuntimeError),
     (sl.StackUnderflowError, RuntimeError),
     (sl.ReadOnlyError, ValueError),
     (sl.VariableUninitializedError, UnboundLocalError),
     (sl.NumArgsError, TypeError),
     (sl.IndexError, IndexError),
     (sl.UsageError, TypeError),
     (sl.ApplicationError, SystemError),
     (sl.InternalError, SystemError),
     (sl.NotImplementedError, NotImplementedError),
     (sl.LimitExceededError, RuntimeError),
     (sl.MathError, ArithmeticError),
     (sl.DivideByZeroError, ZeroDivisionError),
     (sl.ArithOverflowError, OverflowError),
     (sl.ArithUnderflowError, ArithmeticError),
     (sl.DomainError, ArithmeticError),
     (sl.IOError, IOError),
     (sl.WriteError, IOError),
     (sl.ReadError, IOError),
     (sl.OpenError, IOError),
     (sl.DataError, ValueError),
     (sl.UnicodeError, UnicodeError),
     (sl.UTF8Error, UnicodeError),
     (sl.UnknownError, RuntimeError)):

   sl2py_errormap[slerr] = pyerr
